package com.pactera.library.bluetooth

import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGatt
import android.os.Build
import android.bluetooth.BluetoothGattCallback
import android.bluetooth.BluetoothProfile
import android.annotation.TargetApi
import android.app.Activity
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattDescriptor
import android.content.Context
import android.content.Intent
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.util.Log
import com.pactera.library.bluetooth.event.BleManagerEvent
import com.pactera.library.listener.BleNotifyCallback
import com.pactera.library.listener.BleReadCallback
import com.pactera.library.listener.ConnectManagerCallback
import com.pactera.library.utils.BleUtils
import com.pactera.library.utils.SerialDataUtils
import java.util.*
import kotlin.jvm.Synchronized

/**
 * @author ypk
 * 创建日期：2021/9/10  14:29
 * 描述：
 */
class BleManager private constructor() {
    companion object {
        private const val TAG = "BleManager"
        private lateinit var mBleManager: BleManager
        private var mContext: Context? = null

        @Synchronized
        fun getInstance(context: Context?): BleManager {
            if (!this::mBleManager.isInitialized || mBleManager == null) {
                mContext = context
                mBleManager = BleManager()
            }
            return mBleManager
        }
    }

    private val bluetoothAdapter: BluetoothAdapter = BluetoothAdapter.getDefaultAdapter()

    private val UUID_SERVICE =
        UUID.fromString("d973f2e0-b19e-11e2-9e96-0800200c9a66") //关键参数,通信用的uuid 主服务的uuid（必填）
    private val UUID_WRITECHA =
        UUID.fromString("d973f2e2-b19e-11e2-9e96-0800200c9a66") //设置可写特征的uuid （必填,否则写入失败）

    private val UUID_NOTIFY =
        UUID.fromString("d973f2e1-b19e-11e2-9e96-0800200c9a66") //设置notyfy特征的uuid （选填）

    private val UUID_READCHA =
        UUID.fromString("abcd1234-ab12-ab12-ab12-abcdef123456") //设置可读特征的uuid （选填）


    /*private val UUID_SERVICE = UUID.fromString("00001800-0000-1000-8000-00805f9b34fb") //关键参数,通信用的uuid 主服务的uuid（必填）
    private val UUID_WRITECHA = UUID.fromString("00002a00-0000-1000-8000-00805f9b34fb") //设置可写特征的uuid （必填,否则写入失败）
    private val UUID_READCHA = UUID.fromString("00002a04-0000-1000-8000-00805f9b34fb") //设置可读特征的uuid （选填）*/


    fun getDefaultAdapter(): BluetoothAdapter {
        return bluetoothAdapter
    }

    fun enableBLE() {
        //方法二：静默打开
        if (!bluetoothAdapter.isEnabled) {
            //若没打开则打开蓝牙
            bluetoothAdapter.enable()
        }
    }

    fun enableBLE(activity: Activity) {
        //隐式打开，回有弹出提示框
        val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
        activity.startActivityForResult(intent, OPEN_BLE_REQUESTCODE)
    }

    fun startDiscoveryBLE() {
        if (bluetoothAdapter.isDiscovering) { //如果当前在搜索，就先取消搜索
            bluetoothAdapter.cancelDiscovery()
        }
        bluetoothAdapter.startDiscovery() //开启搜索
    }

    fun cancelDiscoveryBLE() {
        if (bluetoothAdapter.isDiscovering) { //如果当前在搜索，就先取消搜索
            bluetoothAdapter.cancelDiscovery()
        }
    }

    fun isEnableBLE(): Boolean {
        return bluetoothAdapter.isEnabled
    }


    /**
     * 发起配对请求
     */
    fun createBond(device: BluetoothDevice?): Boolean {
        cancelDiscoveryBLE();
        //已经测试，这个2个配对方法都可以。
        val result = device?.createBond();//推荐这个原生的
        //val result = ClsUtils.createBond(device1?.javaClass, device1) //通过工具类ClsUtils,调用createBond方法,进行配对
        println("ClientActivity.onCreate result=$result")
        return result ?: false;
    }

    private var connectRetryCount = 3; //连接失败重试次数
    private var connectRetryInterval = 2 * 1000L; //连接失败重试间隔时间
    private var connectCount = 0;

    private val mHandler: Handler = object : Handler(Looper.getMainLooper()) {
        override fun handleMessage(msg: Message) {
            BleUtils.i(TAG, "mHandler gattCallback msg.what=" + msg.what)
            when (msg.what) {
                0 -> {
                    if (connectRetryInterval > 0) {
                        Handler(Looper.getMainLooper()).postDelayed(Runnable {
                            if (connectCount <= connectRetryCount) {
                                connect(globalAddress, mConnectCallback)
                            }
                        }, connectRetryInterval)
                    } else {
                        if (connectCount <= connectRetryCount) {
                            connect(globalAddress, mConnectCallback)
                        }
                    }
                    connectCount++
                }
                else -> {
                }
            }


        }
    }


    var mConnectCallback: ConnectManagerCallback? = null
    var globalAddress = ""
    fun connect(address: String?, mConnectCallback: ConnectManagerCallback?) {

        this.mConnectCallback = mConnectCallback;
        //验证蓝牙 地址的格式 是否正确
        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
            BleUtils.i(TAG, "connect the device address is invalid")
            return
        }

        //如果当前在搜索，就先取消搜索
        cancelDiscoveryBLE()
        globalAddress = address ?: "";

        mConnectCallback?.onConnectionChanged(BleManagerEvent(BleManagerEvent.CONNECTING))

        val device = bluetoothAdapter.getRemoteDevice(address)
        if (device == null) {
            BleUtils.i(TAG, "connect no device")
            return
        }
        val bluetoothGatt =
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//&& device.type == BluetoothDevice.DEVICE_TYPE_DUAL
                device.connectGatt(mContext, false, gattCallback, BluetoothDevice.TRANSPORT_LE)
            } else {
                device.connectGatt(mContext, false, gattCallback)
            }

        if (bluetoothGatt != null) {
            BleUtils.i(TAG, "connect Trying to create a new connection.")
        }
    }

    private val gattCallback: BluetoothGattCallback = object : BluetoothGattCallback() {
        /**
         * ypk:当连接上设备或者失去连接时会回调该函数
         */
        override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
            val device = gatt.device ?: return
            val address = device.address
            Log.i(TAG, "gattCallback onConnectionStateChange address=$address newState=$newState")
            //There is a problem here Every time a new object is generated that causes the same device to be disconnected and the connection produces two objects
            if (status == BluetoothGatt.GATT_SUCCESS) {
                if (newState == BluetoothProfile.STATE_CONNECTED) {
                    BleUtils.i(TAG, "gattCallback onConnectionStateChange success state_connected")
                    //已连接，发现服务中...
                    val mConnectEvent = BleManagerEvent(BleManagerEvent.CONNECTED, device);
                    mConnectCallback?.onConnectionChanged(mConnectEvent)

                    gatt.discoverServices()//去发现服务


                } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                    BleUtils.i(
                        TAG,
                        "gattCallback onConnectionStateChange success state_disconnected"
                    )
                    gatt.close()//关闭

                    //连接断开
                    val mConnectEvent = BleManagerEvent(BleManagerEvent.DISCONNECT, device);
                    mConnectCallback?.onConnectionChanged(mConnectEvent)

                }
            } else {
                //Occurrence 133 or 257 19 Equal value is not 0: Connection establishment failed due to protocol stack
                BleUtils.i(
                    TAG,
                    "gattCallback onConnectionStateChange else status is abnormal=$status"
                )
                gatt.close()//关闭
                mConnectCallback?.onConnectFailed("连接失败", status)

                mConnectCallback?.onConnectionChanged(
                    BleManagerEvent(
                        BleManagerEvent.RECONNECTION,
                        device
                    )
                )

                mHandler.sendEmptyMessage(0)//方案二:直接这里去控制延时也可以，挺好
            }
        }

        /**
         * ypk:当设备找到服务时，会回调该函数
         */
        override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
            BleUtils.i(TAG, "gattCallback onServicesDiscovered status=$status")
            if (status == BluetoothGatt.GATT_SUCCESS) {
                mConnectCallback?.onServicesDiscovered(gatt)

                initReadAndWriteCharacteristic(gatt)
            } else {
                BleUtils.i(TAG, "gattCallback onServicesDiscovered status else")
            }

        }


        /**
         * ypk:
         * 当连接成功的时候会回调这个方法，这个方法可以处理发送密码或者数据分析
         * 当setnotify（true）被设置时，如果MCU（设备端）上的数据改变，则该方法被回调。
         * @param gatt 蓝牙gatt对象
         * @param characteristic 蓝牙通知特征对象
         */
        override fun onCharacteristicChanged(
            gatt: BluetoothGatt,
            characteristic: BluetoothGattCharacteristic
        ) {
            // BleUtils.i(TAG, "gattCallback onCharacteristicChanged 设备发出通知 address=" + gatt.device.address)
            if (gatt?.device == null) return

            bleNotifyCallback?.onChanged(gatt?.device, characteristic)
        }


        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
            BleUtils.i(TAG, "gattCallback onMtuChanged mtu=$mtu,status=$status")
        }


        // 写入 特性数据 操作的回调方法 向BLE蓝牙外设
        override fun onCharacteristicWrite(
            gatt: BluetoothGatt,
            characteristic: BluetoothGattCharacteristic,
            status: Int
        ) {
            // BleUtils.i(TAG, "gattCallback onCharacteristicWrite address=" + gatt.device.address + " status: " + status)
            //if (gatt?.device == null) return
            if (status == BluetoothGatt.GATT_SUCCESS) {
                val uuid = characteristic.uuid
                BleUtils.i(TAG, "gattCallback onCharacteristicWrite 特性写入操作 success  uuid=$uuid")
            } else {
                BleUtils.i(TAG, "gattCallback onCharacteristicWrite failure status=$status")
            }

        }

        // 	mBluetoothGatt.readCharacteristic(characteristic); 调用成功会回调 这里
        // 读取 特性数据 操作的回调方法
        override fun onCharacteristicRead(
            gatt: BluetoothGatt,
            characteristic: BluetoothGattCharacteristic,
            status: Int
        ) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                val uuid = characteristic.uuid
                BleUtils.i(TAG, "gattCallback onCharacteristicRead 特性读取操作 success  uuid=$uuid")
                bleReadCallback?.onReadSuccess(gatt.device, characteristic)
            } else {
                BleUtils.i(TAG, "gattCallback onCharacteristicRead failure status=$status")
                bleReadCallback?.onReadFailed(gatt.device, status)
            }
        }

        // 写入 描述数据 操作的回调结果
        override fun onDescriptorWrite(
            gatt: BluetoothGatt,
            descriptor: BluetoothGattDescriptor,
            status: Int
        ) {
            //if (gatt?.device == null) return
            if (status == BluetoothGatt.GATT_SUCCESS) {
                val uuid = descriptor.characteristic.uuid
                BleUtils.i(TAG, "gattCallback onDescriptorWrite 描述写入操作 success uuid=$uuid")
                // readDescriptor(gatt, descriptor);
            } else {
                BleUtils.i(TAG, "gattCallback onDescriptorWrite failure status=$status")
            }

        }

        // 读取 描述数据 操作的回调方法
        override fun onDescriptorRead(
            gatt: BluetoothGatt,
            descriptor: BluetoothGattDescriptor,
            status: Int
        ) {
            //BleUtils.i(TAG, "gattCallback onDescriptorRead read ")
            //if (gatt == null || gatt.device == null) return
            if (status == BluetoothGatt.GATT_SUCCESS) {
                val uuid = descriptor.characteristic.uuid
                BleUtils.i(TAG, "gattCallback onDescriptorRead read 操作 success uuid=$uuid")
                // readDescriptor(gatt, descriptor);
            } else {
                BleUtils.i(TAG, "gattCallback onDescriptorRead failure status=$status")
            }


        }

        override fun onReadRemoteRssi(gatt: BluetoothGatt, rssi: Int, status: Int) {
            BleUtils.i(TAG, "gattCallback onReadRemoteRssi read remoteRssi, rssi: $rssi")
            //if (gatt == null || gatt.device == null) return
        }
    }


    var mNotifiCharacteristic: BluetoothGattCharacteristic? = null
    var mReadCharacteristic: BluetoothGattCharacteristic? = null
    var mWriteCharacteristic: BluetoothGattCharacteristic? = null
    var mBluetoothGatt: BluetoothGatt? = null

    /**
     * 从发现的Service来找出读数据用的 BluetoothGattCharacteristic 和写数据用的 BluetoothGattCharacteristic。
     * @param gatt
     */
    fun initReadAndWriteCharacteristic(gatt: BluetoothGatt) {
        mBluetoothGatt = gatt;
        val gattServices = gatt.services;
        BleUtils.i(TAG, "initReadAndWriteChar gattServices.size=${gattServices?.size}")
        if (gattServices == null) return
        // 遍历所有的 GATT Services.
        for (gattService in gattServices) {
            val strUUID = gattService.uuid.toString().trim() { it <= ' ' }
            BleUtils.i(TAG, "initReadAndWriteChar service strUUID=$strUUID")

            if (!strUUID.equals(UUID_SERVICE.toString(), ignoreCase = true)) continue

            val gattCharacteristics = gattService.characteristics
            // 遍历当前Service中所有的Characteristics.
            for (gattCharacteristic in gattCharacteristics) {
                val strUUIDCharacteristic = gattCharacteristic.uuid
                BleUtils.i(
                    TAG,
                    "initReadAndWriteChar characteristic strUUID=${
                        strUUIDCharacteristic.toString().trim()
                    }"
                )

                when (strUUIDCharacteristic) {
                    UUID_WRITECHA -> {
                        mWriteCharacteristic = gattCharacteristic
                    }
                    UUID_NOTIFY -> {
                        mNotifiCharacteristic = gattCharacteristic
                        mReadCharacteristic = gattCharacteristic
                        //set 可接收通知的UUID
                        //setCharacteristicNotification(gatt, gattCharacteristic, true);
                    }
                    UUID_READCHA -> {
                        mReadCharacteristic = gattCharacteristic
                    }
                    else -> {

                    }
                }
            }
            val device: BluetoothDevice = gatt.device
            mConnectCallback?.onReady(device)
        }
    }


    var bleNotifyCallback: BleNotifyCallback? = null
    fun setCharacteristicNotification(bleNotifyCallback: BleNotifyCallback, enable: Boolean) {
        this.bleNotifyCallback = bleNotifyCallback;
        mBluetoothGatt?.setCharacteristicNotification(mNotifiCharacteristic, enable)
        for (descriptor in mNotifiCharacteristic!!.descriptors) {
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
            val result = mBluetoothGatt?.writeDescriptor(descriptor)
            BleUtils.i(TAG, "setCharNotification result=$result")
        }
    }

    /**
     * set 可接收通知的UUID，设置其可以接收通知（notification）
     */
    fun setCharacteristicNotification(
        gatt: BluetoothGatt,
        mNoticeCharacteristic: BluetoothGattCharacteristic,
        enable: Boolean
    ) {
        gatt.setCharacteristicNotification(mNoticeCharacteristic, enable)
        for (descriptor in mNotifiCharacteristic!!.descriptors) {
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
            val result = mBluetoothGatt?.writeDescriptor(descriptor)
            println("BleManager.setCharacteristicNotification result=$result")
        }
        /* val descriptor = mNoticeCharacteristic.getDescriptor(UUID_NOTIFY)
         if (descriptor != null) {
             println("BleManager.setCharacteristicNotification descriptor is not null")
             descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
             gatt.writeDescriptor(descriptor)
         }else{
             println("BleManager.setCharacteristicNotification descriptor is  null")
         }*/
    }

    fun sendData(cmd: String) {
        val strCmd = cmd.replace(" ", "")
        Log.i(TAG, "sendData: strCmd=$strCmd")
        val data: ByteArray = SerialDataUtils.HexToByteArr(strCmd)
        mWriteCharacteristic?.setValue(data)
        // mWriteCharacteristic?.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
        mBluetoothGatt?.writeCharacteristic(mWriteCharacteristic)

    }

    var bleReadCallback: BleReadCallback? = null
    fun readData(bleReadCallback: BleReadCallback) { //gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic
        this.bleReadCallback = bleReadCallback;
        if (mReadCharacteristic != null) {
            val readResult = mBluetoothGatt?.readCharacteristic(mReadCharacteristic);
            println("BleManager.readCharacteristic readResult=$readResult")
        } else {
            println("BleManager.readCharacteristic mReadCharacteristic is not init")
        }
    }

    /**
     *关闭蓝牙连接
     */
    fun closeConnect() {
        if (mBluetoothGatt == null) {
            return;
        }
        mBluetoothGatt?.close();
        mBluetoothGatt = null;
    }
    /* fun readCharacteristic(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
        //val readResult = gatt.readCharacteristic(characteristic)
        val bytes = characteristic.value
        val receiveString = SerialDataUtils.ByteArrToHex(bytes).trim()
        println("BleManager.readCharacteristic receiveString=$receiveString")
        //val str = String(bytes)
        //println("BleManager.readCharacteristic str=$str")
    }

    fun readDescriptor(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor) {
        // val readResult = gatt.readDescriptor(descriptor)
        val bytes = descriptor.value
        val receiveString = SerialDataUtils.ByteArrToHex(bytes).trim()
        println("BleManager.readDescriptor receiveString=$receiveString")
    }*/


}